﻿using ArtMath.Component.Line;
using ArtMath.Core.Data;
using ArtMath.Setting;
using System.Collections.Generic;

namespace ArtMath.PointCollection.Helper
{
    public static class Interaction
    {
        public static PointD? GetNearestPoint(this IEnumerable<PointCollectionBase> pcs, PointD point)
        {
            if (pcs == null) return null;
            List<PointD> tmp = new List<PointD>();
            foreach (PointCollectionBase item in pcs)
            {
                PointD? pt = item.GetNearestPoint(point);
                if (pt.HasValue)
                {
                    tmp.Add(pt.Value);
                }
            }
            if (tmp.Count == 0) return null;
            return point.GetNearestPoint(tmp);
        }
        public static PointCollectionBase OnWhichPointCollection(this IEnumerable<PointCollectionBase> pcs, PointD pt)
        {
            if (pcs == null) return null;
            foreach (PointCollectionBase item in pcs)
            {
                if (item.Contains(pt))
                {
                    return item;
                }
            }
            return null;
        }
        public static bool InPointCollections(this IEnumerable<PointCollectionBase> pcs, PointD pt)
        {
            if (pcs == null) return false;
            foreach (PointCollectionBase item in pcs)
            {
                if (item.Contains(pt))
                {
                    return true;
                }
            }
            return false;
        }
        public static void GetCurveIndexAndPointIndex<T>(this IList<T> curs, PointD pt, out int curveIndex, out int pointIndex) where T : Curve
        {
            if (curs != null)
            {
                for (int i = 0; i < curs.Count; i++)
                {
                    pointIndex = curs[i].GetIndex(pt);
                    if (pointIndex >= 0)
                    {
                        curveIndex = i;
                        return;
                    }
                }
            }
            curveIndex = -1;
            pointIndex = -1;
        }
        public static double? GetGeneralCurveTangentLineSlope<T>(this IList<T> curs, PointD pt) where T : Curve
        {
            curs.GetCurveIndexAndPointIndex(pt, out int curveIndex, out int pointIndex);
            if (curveIndex < 0 || pointIndex < 0) return null;
            switch (Settings.Instance.GeneralCurveTangentSlopeSolution)
            {
                case GeneralCurveTangentSlopeSolution.Predecessor:
                    if (curs[curveIndex][pointIndex] == pt)
                    {
                        if (pointIndex == 0)
                        {
                            if (curs[curveIndex].Count > 1)
                            {
                                return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex + 1]);
                            }
                        }
                        else
                        {
                            return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex - 1]);
                        }
                    }
                    else
                    {
                        return pt.GetSlope(curs[curveIndex][pointIndex]);
                    }
                    break;
                case GeneralCurveTangentSlopeSolution.PredecessorAndSuccessor:
                    if (curs[curveIndex][pointIndex] == pt)
                    {
                        if (pointIndex == 0)
                        {
                            if (curs[curveIndex].Count > 1)
                            {
                                return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex + 1]);
                            }
                        }
                        else if (pointIndex == curs[curveIndex].Count - 1)
                        {
                            if (curs[curveIndex].Count > 1)
                            {
                                return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex - 1]);
                            }
                        }
                        else
                        {
                            return curs[curveIndex][pointIndex - 1].GetSlope(curs[curveIndex][pointIndex + 1]);
                        }
                    }
                    else
                    {
                        return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex + 1]);
                    }
                    break;
                case GeneralCurveTangentSlopeSolution.Successor:
                    if (curs[curveIndex][pointIndex] == pt)
                    {
                        if (pointIndex == curs[curveIndex].Count - 1)
                        {
                            if (curs[curveIndex].Count > 1)
                            {
                                return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex - 1]);
                            }
                        }
                        else
                        {
                            return curs[curveIndex][pointIndex].GetSlope(curs[curveIndex][pointIndex + 1]);
                        }
                    }
                    else
                    {
                        return pt.GetSlope(curs[curveIndex][pointIndex + 1]);
                    }
                    break;
            }
            return null;
        }
        public static PointD? GetNearestPointFromCurves<T>(this IEnumerable<T> curs, PointD pt) where T : Curve
        {
            if (curs == null) return null;
            double minDist = double.PositiveInfinity;
            PointD? res = null;
            foreach (T item in curs)
            {
                PointD? tmp = pt.GetNearestPointOnBrokenLine(item, item.IsClosed);
                if (tmp == null) continue;
                double dist = pt.DistanceTo(tmp.Value);
                if (dist < minDist)
                {
                    minDist = dist;
                    res = tmp;
                }
            }
            return res;
        }
    }
}
